import time
import logging
import traceback
import sys
from soaputils import make_fault
from settings import SimulatorSettings

logging.basicConfig()

# MIME Type Headers 
TEXT_PLAIN = ('Content-Type', 'text/plain')
APP_SOAP = ('Content-Type', 'application/soap+xml')

# Response Codes
HTTP_200 = '200 OK'
HTTP_500 = '500 Internal Server Error'

class SimulatorApp(object):
    """WSGI app that responds to SOAP WS requests based 
    SOAPAction header and the configured SimulatorSettings"""

    log = logging.getLogger('soapsim.SimulatorApp')

    def __init__(self, settings):
        """Accepts an initialized SimulatorSettings"""
        self.settings = settings

    def __call__(self, environ, start_response):
        try:
            if environ['REQUEST_METHOD'] == 'GET':
                return self.dump(environ, start_response)
            else:
                return self.simulate(environ, start_response)
        except KeyboardInterrupt:
            raise
        except Exception, e:
            start_response(HTTP_500, [TEXT_PLAIN], sys.exc_info())
            tb = traceback.format_exc()
            msg = "Encountered unexpected exception:\n%s"
            self.log.error(msg % tb)
            return [make_fault(msg[:-3], detail=tb)]

    def dump(self, environ, start_response):
        """Return the current configuration settings"""
        start_response(HTTP_200, [TEXT_PLAIN])
        return ['This is a web service simulator\n',
                self.settings.dump()]

    def simulate(self, environ, start_response):
        soapaction = environ.get('HTTP_SOAPACTION', '').strip('"')
        self.log.info("SOAPAction: %s" % soapaction)
        request = self._read_request(environ)
        self.log.info("Request: %s" % request)
        operation = self.settings.get_operation(soapaction)
        if not operation:
            start_response(HTTP_500, [TEXT_PLAIN]) 
            msg = "Missing operation for SOAPAction: %s" % soapaction
            fault = make_fault(msg, faultcode="Client")
            self.log.error(fault)
            return fault
        response = operation.get_response(request)
        self.log.info("Response: %s" % response)
        self.log.info("Waiting %d seconds" % operation.delay)
        time.sleep(operation.delay)
        self.log.info("Sleep complete")
        start_response(HTTP_200, [APP_SOAP, ("SOAPAction", soapaction)])
        return response

    def _read_request(self, environ):
        return environ['wsgi.input'].read(int(environ['CONTENT_LENGTH']))
